iT邦幫忙

0

Day23修正 Walk-forward 平均準確率的誤導

d23
  • 分享至 

  • xImage
  •  

在分類問題中,當類別極度不平衡時(例如 Target=1 很少),即使模型一直亂猜 Target=0,也能得到看似很高的「準確率」,這具有極大的誤導性。
為了更真實地評估模型對我們最關心的事件 (Target=1,即大幅上漲) 的預測能力,所以在 walk_forward_train 函式中引入 Balanced Accuracy (平衡準確率) 和 Target=1 的 F1-Score 來代替單純的 Accuracy。

Balanced Accuracy (平衡準確率): 衡量模型在每個類別上的召回率 (Recall) 的平均值。它解決了類別大小差異的問題。
F1-Score (Target=1): 是 Target=1 的 Precision(精確度)和 Recall(召回率)的調和平均數。它直接反映了模型在預測「漲幅」這一少數類別上的綜合表現。

from sklearn.metrics import accuracy_score, classification_report, f1_score 

def walk_forward_train(X, y, df, train_window=2000, test_window=400, step=400, model_cls=RandomForestClassifier, model_kwargs=None):
    """
    sliding-window walk-forward training.
    - train_window: 用多少筆資料訓練
    - test_window: 每次測試用多少筆
    - step: 每次往前移動多少筆 (通常 = test_window)
    """
    if model_kwargs is None:
        model_kwargs = {"n_estimators":200, "random_state":42}

    n = len(X)
    folds = []
    f1_scores = []
    last_model = None
    last_test_index = None
    last_y_true = None
    last_y_pred = None

    start = 0
    # 從 train_window 開始,確保有足夠訓練資料
    while start + train_window + test_window <= n:
        train_idx = list(range(start, start + train_window))
        test_idx = list(range(start + train_window, start + train_window + test_window))

        X_train, X_test = X.iloc[train_idx], X.iloc[test_idx]
        y_train, y_test = y.iloc[train_idx], y.iloc[test_idx]

        model = model_cls(**model_kwargs)
        model.fit(X_train, y_train)
        y_pred = model.predict(X_test)

        #計算 Target=1 (pos_label=1) 的 F1-Score
        f1 = f1_score(y_test, y_pred, pos_label=1, zero_division=0)
        f1_scores.append(f1)
        
        # move window
        start += step

        # 保留最後一個 fold 的結果
        last_model = model
        last_test_index = test_idx
        last_y_true = y_test
        last_y_pred = y_pred

        # 為了簡化輸出,這裡不列印每個 Fold 的細節
        # print(f"Fold {len(scores)} Accuracy: {acc:.4f}")

        folds.append({
            "train_index": train_idx,
            "test_index": test_idx,
            "model": model,
            "y_true": y_test,
            "y_pred": y_pred,
            "f1_score": f1
        })

        # move window
        start += step

        # 保留最後一個 fold 的結果
        last_model = model
        last_test_index = test_idx
        last_y_true = y_test
        last_y_pred = y_pred

    print(f"\nWalk-forward 訓練完成。共 {len(f1_scores)} 個 Fold。")

    if f1_scores:
        avg_acc = np.mean(f1_scores)
        print(f"📊 平均準確率: {avg_acc:.4f}")
    else:
        # 如果 scores 為空,則平均準確率為 N/A
        print("📊 平均準確率: N/A (無 Fold 執行)")

    if not folds:
        print("❌ 錯誤:Walk-forward 沒有執行任何 Fold。請檢查資料量是否足夠。")
        return None, None, None, None, None
    
    #新增分類報告,以量化模型在類別不平衡下的性能
    from sklearn.metrics import classification_report
    
    if last_y_pred is not None and len(last_y_pred) > 0:
        print("\n=== 最後一個 Walk-forward 區段的詳細分類報告 (Target=1: 預測漲幅>0.3%) ===")
        # 由於 y_test 和 y_pred 可能是 Series 或 array,直接傳入
        print(classification_report(last_y_true, last_y_pred, 
                                     target_names=['Target=0 (Down/Small)', 'Target=1 (Up)'], 
                                     zero_division=0))
    # 使用最後一個 Fold 的結果繪製預測圖
    plot_predictions(df, last_y_true, last_y_pred, last_test_index, f"Predictions vs Actual (Latest {test_window} bars)")

    return last_model, last_test_index, last_y_true, last_y_pred, folds

當運行程式時輸出將以 平均 F1-Score 為主,這個指標更能反映模型在預測「上漲訊號」時的綜合可靠性,即使原始的總準確率很高(例如 90%),如果平均 F1-Score 很低(例如 0.2),您也會清楚地知道模型的預測能力實際上很差。
以下是執行後的解果
https://ithelp.ithome.com.tw/upload/images/20251013/20178967wioxKf0Pqy.png
Precision (精確度) 0.24 (24%) 當模型預測會上漲時,只有 24% 的時間真的上漲了。
Recall (召回率) 0.14 (14%) 在所有真正上漲的時刻,模型只抓住了 14% 的機會。
F1-Score (平均 F1-Score) 0.17 / 0.1140 綜合表現極差。
F1-Score 越接近 1 越好,這說明模型對上漲的預測幾乎是無效的。

報酬高但勝率低 (159.53% 報酬 / 40% 勝率 / 10 次交易)
高報酬:您的策略總共只交易了 10 次。如果其中有 4 次獲利,但有 1-2 次是極大的獲利(例如 20% 的暴漲),就可以產生 159.53% 的總報酬。
低質量信號:由於 Target=1 的 Precision 只有 24%,這 10 次交易中,多頭進場的信號質量非常差。


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言